﻿// Inner Fire 游戏引擎库
// 场景分装 - 阴影贴图
//
// Copyright (c) 2023 by 尔西大帝. All rights reserved.
//
// 这个类分装各种场景的构建（都在一个文件里面太大了）
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2024-11-29

#include <dx_graphics.h>

namespace ifire {

void DxGraphics::Shadows_Update() {
  //
  // 调整光源
  //
  mLightRotationAngle += 0.1f * g_timer.fixedDeltaTime;

  XMMATRIX R = XMMatrixRotationY(mLightRotationAngle);
  for (int i = 0; i < 3; ++i) {
    XMVECTOR lightDir = XMLoadFloat3(&mBaseLightDirections[i]);
    lightDir = XMVector3TransformNormal(lightDir, R);
    XMStoreFloat3(&mRotatedLightDirections[i], lightDir);
  }

  Shadows_UpdateObjectCBs();
  Shadows_UpdateMaterialBuffer();
  Shadows_UpdateShadowTransform();
  Shadows_UpdateMainPassCB();
  Shadows_UpdateShadowPassCB();
}
void DxGraphics::Shadows_InitPipeline() {
  // 阴影贴图
  mShadowMap = std::make_unique<ShadowMap>(md3dDevice.Get(), 2048, 2048);

  Shadows_LoadTextures();
  Shadows_BuildRootSignature();
  Shadows_BuildDescriptorHeaps();
  Shadows_BuildShadersAndInputLayout();
  Shadows_BuildShapeGeometry();
  Shadows_BuildSkullGeometry();
  Shadows_BuildMaterials();
  Shadows_BuildRenderItems();
  Shadows_BuildFrameResources();
  Shadows_BuildPSOs();
}
void DxGraphics::Shadows_Draw() {
  auto cmdListAlloc = mCurrFrameResource->CmdListAlloc;

  // Reuse the memory associated with command recording.
  // We can only reset when the associated command lists have finished execution
  // on the GPU.
  ThrowIfFailed(cmdListAlloc->Reset());

  // A command list can be reset after it has been added to the command queue
  // via ExecuteCommandList. Reusing the command list reuses memory.
  ThrowIfFailed(mCommandList->Reset(cmdListAlloc.Get(), mPSOs["opaque"].Get()));

  ID3D12DescriptorHeap* descriptorHeaps[] = {mSrvDescriptorHeap.Get()};
  mCommandList->SetDescriptorHeaps(_countof(descriptorHeaps), descriptorHeaps);

  mCommandList->SetGraphicsRootSignature(mRootSignature.Get());

  // Bind all the materials used in this scene.  For structured buffers, we can
  // bypass the heap and set as a root descriptor.
  auto matBuffer = mCurrFrameResource->MaterialBuffer->Resource();
  mCommandList->SetGraphicsRootShaderResourceView(
      2, matBuffer->GetGPUVirtualAddress());

  // Bind null SRV for shadow map pass.
  mCommandList->SetGraphicsRootDescriptorTable(3, mNullSrv);

  // Bind all the textures used in this scene.  Observe
  // that we only have to specify the first descriptor in the table.
  // The root signature knows how many descriptors are expected in the table.
  mCommandList->SetGraphicsRootDescriptorTable(
      4, mSrvDescriptorHeap->GetGPUDescriptorHandleForHeapStart());

  DrawSceneToShadowMap();

  mCommandList->RSSetViewports(1, &mScreenViewport);
  mCommandList->RSSetScissorRects(1, &mScissorRect);

  // Indicate a state transition on the resource usage.
  auto to_render = CD3DX12_RESOURCE_BARRIER::Transition(CurrentBackBuffer(),
      D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET);
  mCommandList->ResourceBarrier(1, &to_render);

  // Clear the back buffer and depth buffer.
  auto back_buffer_view = CurrentBackBufferView();
  auto dsv = DepthStencilView();
  mCommandList->ClearRenderTargetView(
      back_buffer_view, Colors::LightSteelBlue, 0, nullptr);
  mCommandList->ClearDepthStencilView(dsv,
      D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr);

  // Specify the buffers we are going to render to.
  mCommandList->OMSetRenderTargets(1, &back_buffer_view, true, &dsv);

  auto passCB = mCurrFrameResource->PassCB->Resource();
  mCommandList->SetGraphicsRootConstantBufferView(
      1, passCB->GetGPUVirtualAddress());

  // Bind the sky cube map.  For our demos, we just use one "world" cube map
  // representing the environment from far away, so all objects will use the
  // same cube map and we only need to set it once per-frame. If we wanted to
  // use "local" cube maps, we would have to change them per-object, or
  // dynamically index into an array of cube maps.

  CD3DX12_GPU_DESCRIPTOR_HANDLE skyTexDescriptor(
      mSrvDescriptorHeap->GetGPUDescriptorHandleForHeapStart());
  skyTexDescriptor.Offset(mSkyTexHeapIndex, mCbvSrvUavDescriptorSize);
  mCommandList->SetGraphicsRootDescriptorTable(3, skyTexDescriptor);

  mCommandList->SetPipelineState(mPSOs["opaque"].Get());
  Shadows_DrawRenderItems(
      mCommandList.Get(), mRitemLayer[(int)RenderLayer::Opaque]);

  mCommandList->SetPipelineState(mPSOs["debug"].Get());
  Shadows_DrawRenderItems(
      mCommandList.Get(), mRitemLayer[(int)RenderLayer::Debug]);

  mCommandList->SetPipelineState(mPSOs["sky"].Get());
  Shadows_DrawRenderItems(
      mCommandList.Get(), mRitemLayer[(int)RenderLayer::Sky]);

  // Indicate a state transition on the resource usage.
  auto to_present = CD3DX12_RESOURCE_BARRIER::Transition(CurrentBackBuffer(),
      D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT);
  mCommandList->ResourceBarrier(1, &to_present);

  // Done recording commands.
  ThrowIfFailed(mCommandList->Close());

  // Add the command list to the queue for execution.
  ID3D12CommandList* cmdsLists[] = {mCommandList.Get()};
  mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);

  // Swap the back and front buffers
  ThrowIfFailed(mSwapChain->Present(0, 0));
  mCurrBackBuffer = (mCurrBackBuffer + 1) % SwapChainBufferCount;

  // Advance the fence value to mark commands up to this fence point.
  mCurrFrameResource->Fence = ++mCurrentFence;

  // Add an instruction to the command queue to set a new fence point.
  // Because we are on the GPU timeline, the new fence point won't be
  // set until the GPU finishes processing all the commands prior to this
  // Signal().
  mCommandQueue->Signal(mFence.Get(), mCurrentFence);
}
void DxGraphics::Shadows_DrawRenderItems(ID3D12GraphicsCommandList* cmdList,
    const std::vector<RenderItem*>& ritems) {
  DrawRenderItems_V2(cmdList, ritems);
}

void DxGraphics::DrawSceneToShadowMap() {
  auto viewport = mShadowMap->Viewport();
  auto scissor = mShadowMap->ScissorRect();
  mCommandList->RSSetViewports(1, &viewport);
  mCommandList->RSSetScissorRects(1, &scissor);

  // Change to DEPTH_WRITE.
  auto to_write = CD3DX12_RESOURCE_BARRIER::Transition(mShadowMap->Resource(),
      D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_RESOURCE_STATE_DEPTH_WRITE);
  mCommandList->ResourceBarrier(1, &to_write);

  UINT passCBByteSize =
      dxutils::calc_constant_buffer_size(sizeof(PassConstants));

  // Clear the back buffer and depth buffer.
  auto dsv = mShadowMap->Dsv();
  mCommandList->ClearDepthStencilView(dsv,
      D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr);

  // Set null render target because we are only going to draw to
  // depth buffer.  Setting a null render target will disable color writes.
  // Note the active PSO also must specify a render target count of 0.
  mCommandList->OMSetRenderTargets(0, nullptr, false, &dsv);

  // Bind the pass constant buffer for the shadow map pass.
  auto passCB = mCurrFrameResource->PassCB->Resource();
  D3D12_GPU_VIRTUAL_ADDRESS passCBAddress =
      passCB->GetGPUVirtualAddress() + 1 * passCBByteSize;
  mCommandList->SetGraphicsRootConstantBufferView(1, passCBAddress);

  mCommandList->SetPipelineState(mPSOs["shadow_opaque"].Get());

  Shadows_DrawRenderItems(
      mCommandList.Get(), mRitemLayer[(int)RenderLayer::Opaque]);

  // Change back to GENERIC_READ so we can read the texture in a shader.
  auto to_read = CD3DX12_RESOURCE_BARRIER::Transition(mShadowMap->Resource(),
      D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_GENERIC_READ);
  mCommandList->ResourceBarrier(1, &to_read);
}

void DxGraphics::Shadows_LoadTextures() {
  std::vector<std::string> texNames = {"bricksDiffuseMap", "bricksNormalMap",
      "tileDiffuseMap", "tileNormalMap", "defaultDiffuseMap",
      "defaultNormalMap", "skyCubeMap"};

  std::vector<std::string> texFilenames = {"Textures/bricks2.dds",
      "Textures/bricks2_nmap.dds", "Textures/tile.dds",
      "Textures/tile_nmap.dds", "Textures/white1x1.dds",
      "Textures/default_nmap.dds", "Textures/desertcube1024.dds"};

  for (int i = 0; i < (int)texNames.size(); ++i) {
    auto texMap = std::make_unique<Texture>();
    texMap->Name = texNames[i];
    texMap->Filename = texFilenames[i];
    ThrowIfFailed(LoadDDS(
        texMap->Filename.c_str(), texMap->Resource, texMap->UploadHeap));

    mTextures[texMap->Name] = std::move(texMap);
  }
}
void DxGraphics::Shadows_BuildRootSignature() {
  CD3DX12_DESCRIPTOR_RANGE texTable0;
  texTable0.Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 2, 0, 0);

  CD3DX12_DESCRIPTOR_RANGE texTable1;
  texTable1.Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 10, 2, 0);

  // Root parameter can be a table, root descriptor or root constants.
  CD3DX12_ROOT_PARAMETER slotRootParameter[5];

  // Perfomance TIP: Order from most frequent to least frequent.
  slotRootParameter[0].InitAsConstantBufferView(0);
  slotRootParameter[1].InitAsConstantBufferView(1);
  slotRootParameter[2].InitAsShaderResourceView(0, 1);
  slotRootParameter[3].InitAsDescriptorTable(
      1, &texTable0, D3D12_SHADER_VISIBILITY_PIXEL);
  slotRootParameter[4].InitAsDescriptorTable(
      1, &texTable1, D3D12_SHADER_VISIBILITY_PIXEL);

  auto staticSamplers = Shadows_GetStaticSamplers();

  // A root signature is an array of root parameters.
  CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc(5, slotRootParameter,
      (UINT)staticSamplers.size(), staticSamplers.data(),
      D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);

  // create a root signature with a single slot which points to a descriptor
  // range consisting of a single constant buffer
  ComPtr<ID3DBlob> serializedRootSig = nullptr;
  ComPtr<ID3DBlob> errorBlob = nullptr;
  HRESULT hr =
      D3D12SerializeRootSignature(&rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1,
          serializedRootSig.GetAddressOf(), errorBlob.GetAddressOf());

  if (errorBlob != nullptr) {
    ::OutputDebugStringA((char*)errorBlob->GetBufferPointer());
  }
  ThrowIfFailed(hr);

  ThrowIfFailed(md3dDevice->CreateRootSignature(0,
      serializedRootSig->GetBufferPointer(), serializedRootSig->GetBufferSize(),
      IID_PPV_ARGS(mRootSignature.GetAddressOf())));
}
void DxGraphics::Shadows_BuildDescriptorHeaps() {
  //
  // Create the SRV heap.
  //
  D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {};
  srvHeapDesc.NumDescriptors = 14;
  srvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
  srvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
  ThrowIfFailed(md3dDevice->CreateDescriptorHeap(
      &srvHeapDesc, IID_PPV_ARGS(&mSrvDescriptorHeap)));

  //
  // Fill out the heap with actual descriptors.
  //
  CD3DX12_CPU_DESCRIPTOR_HANDLE hDescriptor(
      mSrvDescriptorHeap->GetCPUDescriptorHandleForHeapStart());

  std::vector<ComPtr<ID3D12Resource>> tex2DList = {
      mTextures["bricksDiffuseMap"]->Resource,
      mTextures["bricksNormalMap"]->Resource,
      mTextures["tileDiffuseMap"]->Resource,
      mTextures["tileNormalMap"]->Resource,
      mTextures["defaultDiffuseMap"]->Resource,
      mTextures["defaultNormalMap"]->Resource};

  auto skyCubeMap = mTextures["skyCubeMap"]->Resource;

  D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
  srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
  srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
  srvDesc.Texture2D.MostDetailedMip = 0;
  srvDesc.Texture2D.ResourceMinLODClamp = 0.0f;

  for (UINT i = 0; i < (UINT)tex2DList.size(); ++i) {
    srvDesc.Format = tex2DList[i]->GetDesc().Format;
    srvDesc.Texture2D.MipLevels = tex2DList[i]->GetDesc().MipLevels;
    md3dDevice->CreateShaderResourceView(
        tex2DList[i].Get(), &srvDesc, hDescriptor);

    // next descriptor
    hDescriptor.Offset(1, mCbvSrvUavDescriptorSize);
  }

  srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
  srvDesc.TextureCube.MostDetailedMip = 0;
  srvDesc.TextureCube.MipLevels = skyCubeMap->GetDesc().MipLevels;
  srvDesc.TextureCube.ResourceMinLODClamp = 0.0f;
  srvDesc.Format = skyCubeMap->GetDesc().Format;
  md3dDevice->CreateShaderResourceView(skyCubeMap.Get(), &srvDesc, hDescriptor);

  mSkyTexHeapIndex = (UINT)tex2DList.size();
  mShadowMapHeapIndex = mSkyTexHeapIndex + 1;

  mNullCubeSrvIndex = mShadowMapHeapIndex + 1;
  mNullTexSrvIndex = mNullCubeSrvIndex + 1;

  auto srvCpuStart = mSrvDescriptorHeap->GetCPUDescriptorHandleForHeapStart();
  auto srvGpuStart = mSrvDescriptorHeap->GetGPUDescriptorHandleForHeapStart();
  auto dsvCpuStart = mDsvHeap->GetCPUDescriptorHandleForHeapStart();

  auto nullSrv = CD3DX12_CPU_DESCRIPTOR_HANDLE(
      srvCpuStart, mNullCubeSrvIndex, mCbvSrvUavDescriptorSize);
  mNullSrv = CD3DX12_GPU_DESCRIPTOR_HANDLE(
      srvGpuStart, mNullCubeSrvIndex, mCbvSrvUavDescriptorSize);

  md3dDevice->CreateShaderResourceView(nullptr, &srvDesc, nullSrv);
  nullSrv.Offset(1, mCbvSrvUavDescriptorSize);

  srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
  srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
  srvDesc.Texture2D.MostDetailedMip = 0;
  srvDesc.Texture2D.MipLevels = 1;
  srvDesc.Texture2D.ResourceMinLODClamp = 0.0f;
  md3dDevice->CreateShaderResourceView(nullptr, &srvDesc, nullSrv);

  mShadowMap->BuildDescriptors(
      CD3DX12_CPU_DESCRIPTOR_HANDLE(
          srvCpuStart, mShadowMapHeapIndex, mCbvSrvUavDescriptorSize),
      CD3DX12_GPU_DESCRIPTOR_HANDLE(
          srvGpuStart, mShadowMapHeapIndex, mCbvSrvUavDescriptorSize),
      CD3DX12_CPU_DESCRIPTOR_HANDLE(dsvCpuStart, 1, mDsvDescriptorSize));
}
void DxGraphics::Shadows_BuildShadersAndInputLayout() {
  const D3D_SHADER_MACRO alphaTestDefines[] = {"ALPHA_TEST", "1", NULL, NULL};

  mShaders["standardVS"] = d3dUtil::CompileShader(
      "Shaders\\Shadows_Default.hlsl", nullptr, "VS", "vs_5_1");
  mShaders["opaquePS"] = d3dUtil::CompileShader(
      "Shaders\\Shadows_Default.hlsl", nullptr, "PS", "ps_5_1");

  mShaders["shadowVS"] =
      d3dUtil::CompileShader("Shaders\\Shadows.hlsl", nullptr, "VS", "vs_5_1");
  mShaders["shadowOpaquePS"] =
      d3dUtil::CompileShader("Shaders\\Shadows.hlsl", nullptr, "PS", "ps_5_1");
  mShaders["shadowAlphaTestedPS"] = d3dUtil::CompileShader(
      "Shaders\\Shadows.hlsl", alphaTestDefines, "PS", "ps_5_1");

  mShaders["debugVS"] = d3dUtil::CompileShader(
      "Shaders\\Shadow_Debug.hlsl", nullptr, "VS", "vs_5_1");
  mShaders["debugPS"] = d3dUtil::CompileShader(
      "Shaders\\Shadow_Debug.hlsl", nullptr, "PS", "ps_5_1");

  mShaders["skyVS"] = d3dUtil::CompileShader(
      "Shaders\\Shadows_Sky.hlsl", nullptr, "VS", "vs_5_1");
  mShaders["skyPS"] = d3dUtil::CompileShader(
      "Shaders\\Shadows_Sky.hlsl", nullptr, "PS", "ps_5_1");

  mInputLayout = {
      {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,
          D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
      {"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12,
          D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
      {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24,
          D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
      {"TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 32,
          D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
  };
}
void DxGraphics::Shadows_BuildShapeGeometry() {
  GeometryGenerator geoGen;
  GeometryGenerator::MeshData box = geoGen.CreateBox(1.0f, 1.0f, 1.0f, 3);
  GeometryGenerator::MeshData grid = geoGen.CreateGrid(20.0f, 30.0f, 60, 40);
  GeometryGenerator::MeshData sphere = geoGen.CreateSphere(0.5f, 20, 20);
  GeometryGenerator::MeshData cylinder =
      geoGen.CreateCylinder(0.5f, 0.3f, 3.0f, 20, 20);
  GeometryGenerator::MeshData quad =
      geoGen.CreateQuad(0.0f, 0.0f, 1.0f, 1.0f, 0.0f);

  //
  // We are concatenating all the geometry into one big vertex/index buffer.  So
  // define the regions in the buffer each submesh covers.
  //

  // Cache the vertex offsets to each object in the concatenated vertex buffer.
  UINT boxVertexOffset = 0;
  UINT gridVertexOffset = (UINT)box.Vertices.size();
  UINT sphereVertexOffset = gridVertexOffset + (UINT)grid.Vertices.size();
  UINT cylinderVertexOffset = sphereVertexOffset + (UINT)sphere.Vertices.size();
  UINT quadVertexOffset = cylinderVertexOffset + (UINT)cylinder.Vertices.size();

  // Cache the starting index for each object in the concatenated index buffer.
  UINT boxIndexOffset = 0;
  UINT gridIndexOffset = (UINT)box.Indices32.size();
  UINT sphereIndexOffset = gridIndexOffset + (UINT)grid.Indices32.size();
  UINT cylinderIndexOffset = sphereIndexOffset + (UINT)sphere.Indices32.size();
  UINT quadIndexOffset = cylinderIndexOffset + (UINT)cylinder.Indices32.size();

  SubmeshGeometry boxSubmesh;
  boxSubmesh.IndexCount = (UINT)box.Indices32.size();
  boxSubmesh.StartIndexLocation = boxIndexOffset;
  boxSubmesh.BaseVertexLocation = boxVertexOffset;

  SubmeshGeometry gridSubmesh;
  gridSubmesh.IndexCount = (UINT)grid.Indices32.size();
  gridSubmesh.StartIndexLocation = gridIndexOffset;
  gridSubmesh.BaseVertexLocation = gridVertexOffset;

  SubmeshGeometry sphereSubmesh;
  sphereSubmesh.IndexCount = (UINT)sphere.Indices32.size();
  sphereSubmesh.StartIndexLocation = sphereIndexOffset;
  sphereSubmesh.BaseVertexLocation = sphereVertexOffset;

  SubmeshGeometry cylinderSubmesh;
  cylinderSubmesh.IndexCount = (UINT)cylinder.Indices32.size();
  cylinderSubmesh.StartIndexLocation = cylinderIndexOffset;
  cylinderSubmesh.BaseVertexLocation = cylinderVertexOffset;

  SubmeshGeometry quadSubmesh;
  quadSubmesh.IndexCount = (UINT)quad.Indices32.size();
  quadSubmesh.StartIndexLocation = quadIndexOffset;
  quadSubmesh.BaseVertexLocation = quadVertexOffset;

  //
  // Extract the vertex elements we are interested in and pack the
  // vertices of all the meshes into one vertex buffer.
  //

  auto totalVertexCount = box.Vertices.size() + grid.Vertices.size() +
                          sphere.Vertices.size() + cylinder.Vertices.size() +
                          quad.Vertices.size();

  std::vector<VertexV2> vertices(totalVertexCount);

  UINT k = 0;
  for (size_t i = 0; i < box.Vertices.size(); ++i, ++k) {
    vertices[k].Pos = box.Vertices[i].Position;
    vertices[k].Normal = box.Vertices[i].Normal;
    vertices[k].TexC = box.Vertices[i].TexC;
    vertices[k].TangentU = box.Vertices[i].TangentU;
  }

  for (size_t i = 0; i < grid.Vertices.size(); ++i, ++k) {
    vertices[k].Pos = grid.Vertices[i].Position;
    vertices[k].Normal = grid.Vertices[i].Normal;
    vertices[k].TexC = grid.Vertices[i].TexC;
    vertices[k].TangentU = grid.Vertices[i].TangentU;
  }

  for (size_t i = 0; i < sphere.Vertices.size(); ++i, ++k) {
    vertices[k].Pos = sphere.Vertices[i].Position;
    vertices[k].Normal = sphere.Vertices[i].Normal;
    vertices[k].TexC = sphere.Vertices[i].TexC;
    vertices[k].TangentU = sphere.Vertices[i].TangentU;
  }

  for (size_t i = 0; i < cylinder.Vertices.size(); ++i, ++k) {
    vertices[k].Pos = cylinder.Vertices[i].Position;
    vertices[k].Normal = cylinder.Vertices[i].Normal;
    vertices[k].TexC = cylinder.Vertices[i].TexC;
    vertices[k].TangentU = cylinder.Vertices[i].TangentU;
  }

  for (int i = 0; i < quad.Vertices.size(); ++i, ++k) {
    vertices[k].Pos = quad.Vertices[i].Position;
    vertices[k].Normal = quad.Vertices[i].Normal;
    vertices[k].TexC = quad.Vertices[i].TexC;
    vertices[k].TangentU = quad.Vertices[i].TangentU;
  }

  std::vector<std::uint16_t> indices;
  indices.insert(indices.end(), std::begin(box.GetIndices16()),
      std::end(box.GetIndices16()));
  indices.insert(indices.end(), std::begin(grid.GetIndices16()),
      std::end(grid.GetIndices16()));
  indices.insert(indices.end(), std::begin(sphere.GetIndices16()),
      std::end(sphere.GetIndices16()));
  indices.insert(indices.end(), std::begin(cylinder.GetIndices16()),
      std::end(cylinder.GetIndices16()));
  indices.insert(indices.end(), std::begin(quad.GetIndices16()),
      std::end(quad.GetIndices16()));

  const UINT vbByteSize = (UINT)vertices.size() * sizeof(VertexV2);
  const UINT ibByteSize = (UINT)indices.size() * sizeof(std::uint16_t);

  auto geo = std::make_unique<MeshGeometry>();
  geo->Name = "shapeGeo";

  ThrowIfFailed(D3DCreateBlob(vbByteSize, &geo->VertexBufferCPU));
  CopyMemory(
      geo->VertexBufferCPU->GetBufferPointer(), vertices.data(), vbByteSize);

  ThrowIfFailed(D3DCreateBlob(ibByteSize, &geo->IndexBufferCPU));
  CopyMemory(
      geo->IndexBufferCPU->GetBufferPointer(), indices.data(), ibByteSize);

  geo->VertexBufferGPU = CreateDefaultBuffer(
      vertices.data(), vbByteSize, geo->VertexBufferUploader);

  geo->IndexBufferGPU =
      CreateDefaultBuffer(indices.data(), ibByteSize, geo->IndexBufferUploader);

  geo->VertexByteStride = sizeof(VertexV2);
  geo->VertexBufferByteSize = vbByteSize;
  geo->IndexFormat = DXGI_FORMAT_R16_UINT;
  geo->IndexBufferByteSize = ibByteSize;

  geo->DrawArgs["box"] = boxSubmesh;
  geo->DrawArgs["grid"] = gridSubmesh;
  geo->DrawArgs["sphere"] = sphereSubmesh;
  geo->DrawArgs["cylinder"] = cylinderSubmesh;
  geo->DrawArgs["quad"] = quadSubmesh;

  mGeometries[geo->Name] = std::move(geo);
}
void DxGraphics::Shadows_BuildSkullGeometry() {
  std::ifstream fin("Models/skull.txt");

  if (!fin) {
    MessageBox(0, "Models/skull.txt not found.", 0, 0);
    return;
  }

  UINT vcount = 0;
  UINT tcount = 0;
  std::string ignore;

  fin >> ignore >> vcount;
  fin >> ignore >> tcount;
  fin >> ignore >> ignore >> ignore >> ignore;

  XMFLOAT3 vMinf3(
      +MathHelper::Infinity, +MathHelper::Infinity, +MathHelper::Infinity);
  XMFLOAT3 vMaxf3(
      -MathHelper::Infinity, -MathHelper::Infinity, -MathHelper::Infinity);

  XMVECTOR vMin = XMLoadFloat3(&vMinf3);
  XMVECTOR vMax = XMLoadFloat3(&vMaxf3);

  std::vector<VertexV2> vertices(vcount);
  for (UINT i = 0; i < vcount; ++i) {
    fin >> vertices[i].Pos.x >> vertices[i].Pos.y >> vertices[i].Pos.z;
    fin >> vertices[i].Normal.x >> vertices[i].Normal.y >> vertices[i].Normal.z;

    vertices[i].TexC = {0.0f, 0.0f};

    XMVECTOR P = XMLoadFloat3(&vertices[i].Pos);

    XMVECTOR N = XMLoadFloat3(&vertices[i].Normal);

    // Generate a tangent vector so normal mapping works.  We aren't applying
    // a texture map to the skull, so we just need any tangent vector so that
    // the math works out to give us the original interpolated vertex normal.
    XMVECTOR up = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
    if (fabsf(XMVectorGetX(XMVector3Dot(N, up))) < 1.0f - 0.001f) {
      XMVECTOR T = XMVector3Normalize(XMVector3Cross(up, N));
      XMStoreFloat3(&vertices[i].TangentU, T);
    } else {
      up = XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f);
      XMVECTOR T = XMVector3Normalize(XMVector3Cross(N, up));
      XMStoreFloat3(&vertices[i].TangentU, T);
    }

    vMin = XMVectorMin(vMin, P);
    vMax = XMVectorMax(vMax, P);
  }

  BoundingBox bounds;
  XMStoreFloat3(&bounds.Center, 0.5f * (vMin + vMax));
  XMStoreFloat3(&bounds.Extents, 0.5f * (vMax - vMin));

  fin >> ignore;
  fin >> ignore;
  fin >> ignore;

  std::vector<std::int32_t> indices(3 * tcount);
  for (UINT i = 0; i < tcount; ++i) {
    fin >> indices[i * 3 + 0] >> indices[i * 3 + 1] >> indices[i * 3 + 2];
  }

  fin.close();

  //
  // Pack the indices of all the meshes into one index buffer.
  //

  const UINT vbByteSize = (UINT)vertices.size() * sizeof(VertexV2);

  const UINT ibByteSize = (UINT)indices.size() * sizeof(std::int32_t);

  auto geo = std::make_unique<MeshGeometry>();
  geo->Name = "skullGeo";

  ThrowIfFailed(D3DCreateBlob(vbByteSize, &geo->VertexBufferCPU));
  CopyMemory(
      geo->VertexBufferCPU->GetBufferPointer(), vertices.data(), vbByteSize);

  ThrowIfFailed(D3DCreateBlob(ibByteSize, &geo->IndexBufferCPU));
  CopyMemory(
      geo->IndexBufferCPU->GetBufferPointer(), indices.data(), ibByteSize);

  geo->VertexBufferGPU = CreateDefaultBuffer(
      vertices.data(), vbByteSize, geo->VertexBufferUploader);

  geo->IndexBufferGPU =
      CreateDefaultBuffer(indices.data(), ibByteSize, geo->IndexBufferUploader);

  geo->VertexByteStride = sizeof(VertexV2);
  geo->VertexBufferByteSize = vbByteSize;
  geo->IndexFormat = DXGI_FORMAT_R32_UINT;
  geo->IndexBufferByteSize = ibByteSize;

  SubmeshGeometry submesh;
  submesh.IndexCount = (UINT)indices.size();
  submesh.StartIndexLocation = 0;
  submesh.BaseVertexLocation = 0;
  submesh.Bounds = bounds;

  geo->DrawArgs["skull"] = submesh;

  mGeometries[geo->Name] = std::move(geo);
}
void DxGraphics::Shadows_BuildMaterials() {
  auto bricks0 = std::make_unique<Material>();
  bricks0->Name = "bricks0";
  bricks0->MatCBIndex = 0;
  bricks0->DiffuseSrvHeapIndex = 0;
  bricks0->NormalSrvHeapIndex = 1;
  bricks0->DiffuseAlbedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
  bricks0->FresnelR0 = XMFLOAT3(0.1f, 0.1f, 0.1f);
  bricks0->Roughness = 0.3f;

  auto tile0 = std::make_unique<Material>();
  tile0->Name = "tile0";
  tile0->MatCBIndex = 1;
  tile0->DiffuseSrvHeapIndex = 2;
  tile0->NormalSrvHeapIndex = 3;
  tile0->DiffuseAlbedo = XMFLOAT4(0.9f, 0.9f, 0.9f, 1.0f);
  tile0->FresnelR0 = XMFLOAT3(0.2f, 0.2f, 0.2f);
  tile0->Roughness = 0.1f;

  auto mirror0 = std::make_unique<Material>();
  mirror0->Name = "mirror0";
  mirror0->MatCBIndex = 2;
  mirror0->DiffuseSrvHeapIndex = 4;
  mirror0->NormalSrvHeapIndex = 5;
  mirror0->DiffuseAlbedo = XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f);
  mirror0->FresnelR0 = XMFLOAT3(0.98f, 0.97f, 0.95f);
  mirror0->Roughness = 0.1f;

  auto skullMat = std::make_unique<Material>();
  skullMat->Name = "skullMat";
  skullMat->MatCBIndex = 3;
  skullMat->DiffuseSrvHeapIndex = 4;
  skullMat->NormalSrvHeapIndex = 5;
  skullMat->DiffuseAlbedo = XMFLOAT4(0.3f, 0.3f, 0.3f, 1.0f);
  skullMat->FresnelR0 = XMFLOAT3(0.6f, 0.6f, 0.6f);
  skullMat->Roughness = 0.2f;

  auto sky = std::make_unique<Material>();
  sky->Name = "sky";
  sky->MatCBIndex = 4;
  sky->DiffuseSrvHeapIndex = 6;
  sky->NormalSrvHeapIndex = 7;
  sky->DiffuseAlbedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
  sky->FresnelR0 = XMFLOAT3(0.1f, 0.1f, 0.1f);
  sky->Roughness = 1.0f;

  mMaterials["bricks0"] = std::move(bricks0);
  mMaterials["tile0"] = std::move(tile0);
  mMaterials["mirror0"] = std::move(mirror0);
  mMaterials["skullMat"] = std::move(skullMat);
  mMaterials["sky"] = std::move(sky);
}
void DxGraphics::Shadows_BuildRenderItems() {
  auto skyRitem = std::make_unique<RenderItem>();
  XMStoreFloat4x4(&skyRitem->World, XMMatrixScaling(5000.0f, 5000.0f, 5000.0f));
  skyRitem->TexTransform = MathHelper::Identity4x4();
  skyRitem->ObjCBIndex = 0;
  skyRitem->Mat = mMaterials["sky"].get();
  skyRitem->Geo = mGeometries["shapeGeo"].get();
  skyRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  skyRitem->IndexCount = skyRitem->Geo->DrawArgs["sphere"].IndexCount;
  skyRitem->StartIndexLocation =
      skyRitem->Geo->DrawArgs["sphere"].StartIndexLocation;
  skyRitem->BaseVertexLocation =
      skyRitem->Geo->DrawArgs["sphere"].BaseVertexLocation;

  mRitemLayer[(int)RenderLayer::Sky].push_back(skyRitem.get());
  mAllRitems.push_back(std::move(skyRitem));

  auto quadRitem = std::make_unique<RenderItem>();
  quadRitem->World = MathHelper::Identity4x4();
  quadRitem->TexTransform = MathHelper::Identity4x4();
  quadRitem->ObjCBIndex = 1;
  quadRitem->Mat = mMaterials["bricks0"].get();
  quadRitem->Geo = mGeometries["shapeGeo"].get();
  quadRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  quadRitem->IndexCount = quadRitem->Geo->DrawArgs["quad"].IndexCount;
  quadRitem->StartIndexLocation =
      quadRitem->Geo->DrawArgs["quad"].StartIndexLocation;
  quadRitem->BaseVertexLocation =
      quadRitem->Geo->DrawArgs["quad"].BaseVertexLocation;

  mRitemLayer[(int)RenderLayer::Debug].push_back(quadRitem.get());
  mAllRitems.push_back(std::move(quadRitem));

  auto boxRitem = std::make_unique<RenderItem>();
  XMStoreFloat4x4(&boxRitem->World, XMMatrixScaling(2.0f, 1.0f, 2.0f) *
                                        XMMatrixTranslation(0.0f, 0.5f, 0.0f));
  XMStoreFloat4x4(&boxRitem->TexTransform, XMMatrixScaling(1.0f, 0.5f, 1.0f));
  boxRitem->ObjCBIndex = 2;
  boxRitem->Mat = mMaterials["bricks0"].get();
  boxRitem->Geo = mGeometries["shapeGeo"].get();
  boxRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  boxRitem->IndexCount = boxRitem->Geo->DrawArgs["box"].IndexCount;
  boxRitem->StartIndexLocation =
      boxRitem->Geo->DrawArgs["box"].StartIndexLocation;
  boxRitem->BaseVertexLocation =
      boxRitem->Geo->DrawArgs["box"].BaseVertexLocation;

  mRitemLayer[(int)RenderLayer::Opaque].push_back(boxRitem.get());
  mAllRitems.push_back(std::move(boxRitem));

  auto skullRitem = std::make_unique<RenderItem>();
  XMStoreFloat4x4(
      &skullRitem->World, XMMatrixScaling(0.4f, 0.4f, 0.4f) *
                              XMMatrixTranslation(0.0f, 1.0f, 0.0f));
  skullRitem->TexTransform = MathHelper::Identity4x4();
  skullRitem->ObjCBIndex = 3;
  skullRitem->Mat = mMaterials["skullMat"].get();
  skullRitem->Geo = mGeometries["skullGeo"].get();
  skullRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  skullRitem->IndexCount = skullRitem->Geo->DrawArgs["skull"].IndexCount;
  skullRitem->StartIndexLocation =
      skullRitem->Geo->DrawArgs["skull"].StartIndexLocation;
  skullRitem->BaseVertexLocation =
      skullRitem->Geo->DrawArgs["skull"].BaseVertexLocation;

  mRitemLayer[(int)RenderLayer::Opaque].push_back(skullRitem.get());
  mAllRitems.push_back(std::move(skullRitem));

  auto gridRitem = std::make_unique<RenderItem>();
  gridRitem->World = MathHelper::Identity4x4();
  XMStoreFloat4x4(&gridRitem->TexTransform, XMMatrixScaling(8.0f, 8.0f, 1.0f));
  gridRitem->ObjCBIndex = 4;
  gridRitem->Mat = mMaterials["tile0"].get();
  gridRitem->Geo = mGeometries["shapeGeo"].get();
  gridRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  gridRitem->IndexCount = gridRitem->Geo->DrawArgs["grid"].IndexCount;
  gridRitem->StartIndexLocation =
      gridRitem->Geo->DrawArgs["grid"].StartIndexLocation;
  gridRitem->BaseVertexLocation =
      gridRitem->Geo->DrawArgs["grid"].BaseVertexLocation;

  mRitemLayer[(int)RenderLayer::Opaque].push_back(gridRitem.get());
  mAllRitems.push_back(std::move(gridRitem));

  XMMATRIX brickTexTransform = XMMatrixScaling(1.5f, 2.0f, 1.0f);
  UINT objCBIndex = 5;
  for (int i = 0; i < 5; ++i) {
    auto leftCylRitem = std::make_unique<RenderItem>();
    auto rightCylRitem = std::make_unique<RenderItem>();
    auto leftSphereRitem = std::make_unique<RenderItem>();
    auto rightSphereRitem = std::make_unique<RenderItem>();

    XMMATRIX leftCylWorld = XMMatrixTranslation(-5.0f, 1.5f, -10.0f + i * 5.0f);
    XMMATRIX rightCylWorld =
        XMMatrixTranslation(+5.0f, 1.5f, -10.0f + i * 5.0f);

    XMMATRIX leftSphereWorld =
        XMMatrixTranslation(-5.0f, 3.5f, -10.0f + i * 5.0f);
    XMMATRIX rightSphereWorld =
        XMMatrixTranslation(+5.0f, 3.5f, -10.0f + i * 5.0f);

    XMStoreFloat4x4(&leftCylRitem->World, rightCylWorld);
    XMStoreFloat4x4(&leftCylRitem->TexTransform, brickTexTransform);
    leftCylRitem->ObjCBIndex = objCBIndex++;
    leftCylRitem->Mat = mMaterials["bricks0"].get();
    leftCylRitem->Geo = mGeometries["shapeGeo"].get();
    leftCylRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
    leftCylRitem->IndexCount =
        leftCylRitem->Geo->DrawArgs["cylinder"].IndexCount;
    leftCylRitem->StartIndexLocation =
        leftCylRitem->Geo->DrawArgs["cylinder"].StartIndexLocation;
    leftCylRitem->BaseVertexLocation =
        leftCylRitem->Geo->DrawArgs["cylinder"].BaseVertexLocation;

    XMStoreFloat4x4(&rightCylRitem->World, leftCylWorld);
    XMStoreFloat4x4(&rightCylRitem->TexTransform, brickTexTransform);
    rightCylRitem->ObjCBIndex = objCBIndex++;
    rightCylRitem->Mat = mMaterials["bricks0"].get();
    rightCylRitem->Geo = mGeometries["shapeGeo"].get();
    rightCylRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
    rightCylRitem->IndexCount =
        rightCylRitem->Geo->DrawArgs["cylinder"].IndexCount;
    rightCylRitem->StartIndexLocation =
        rightCylRitem->Geo->DrawArgs["cylinder"].StartIndexLocation;
    rightCylRitem->BaseVertexLocation =
        rightCylRitem->Geo->DrawArgs["cylinder"].BaseVertexLocation;

    XMStoreFloat4x4(&leftSphereRitem->World, leftSphereWorld);
    leftSphereRitem->TexTransform = MathHelper::Identity4x4();
    leftSphereRitem->ObjCBIndex = objCBIndex++;
    leftSphereRitem->Mat = mMaterials["mirror0"].get();
    leftSphereRitem->Geo = mGeometries["shapeGeo"].get();
    leftSphereRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
    leftSphereRitem->IndexCount =
        leftSphereRitem->Geo->DrawArgs["sphere"].IndexCount;
    leftSphereRitem->StartIndexLocation =
        leftSphereRitem->Geo->DrawArgs["sphere"].StartIndexLocation;
    leftSphereRitem->BaseVertexLocation =
        leftSphereRitem->Geo->DrawArgs["sphere"].BaseVertexLocation;

    XMStoreFloat4x4(&rightSphereRitem->World, rightSphereWorld);
    rightSphereRitem->TexTransform = MathHelper::Identity4x4();
    rightSphereRitem->ObjCBIndex = objCBIndex++;
    rightSphereRitem->Mat = mMaterials["mirror0"].get();
    rightSphereRitem->Geo = mGeometries["shapeGeo"].get();
    rightSphereRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
    rightSphereRitem->IndexCount =
        rightSphereRitem->Geo->DrawArgs["sphere"].IndexCount;
    rightSphereRitem->StartIndexLocation =
        rightSphereRitem->Geo->DrawArgs["sphere"].StartIndexLocation;
    rightSphereRitem->BaseVertexLocation =
        rightSphereRitem->Geo->DrawArgs["sphere"].BaseVertexLocation;

    mRitemLayer[(int)RenderLayer::Opaque].push_back(leftCylRitem.get());
    mRitemLayer[(int)RenderLayer::Opaque].push_back(rightCylRitem.get());
    mRitemLayer[(int)RenderLayer::Opaque].push_back(leftSphereRitem.get());
    mRitemLayer[(int)RenderLayer::Opaque].push_back(rightSphereRitem.get());

    mAllRitems.push_back(std::move(leftCylRitem));
    mAllRitems.push_back(std::move(rightCylRitem));
    mAllRitems.push_back(std::move(leftSphereRitem));
    mAllRitems.push_back(std::move(rightSphereRitem));
  }
}
void DxGraphics::Shadows_BuildFrameResources() {
  FrameResourceDesc desc = {};
  desc.PassCount = 2;
  desc.ObjectCount = static_cast<UINT>(items_.size());
  desc.MaterialCount = static_cast<UINT>(materials_.size());

  for (int i = 0; i < FRAME_RESOURCE_COUNT; i++) {
    frame_resources_.push_back(
        std::make_unique<FrameResource>(device_.Get(), &desc));
  }
}
void DxGraphics::Shadows_BuildPSOs() {
  D3D12_GRAPHICS_PIPELINE_STATE_DESC opaquePsoDesc;

  //
  // PSO for opaque objects.
  //
  ZeroMemory(&opaquePsoDesc, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
  opaquePsoDesc.InputLayout = {mInputLayout.data(), (UINT)mInputLayout.size()};
  opaquePsoDesc.pRootSignature = mRootSignature.Get();
  opaquePsoDesc.VS = {
      reinterpret_cast<BYTE*>(mShaders["standardVS"]->GetBufferPointer()),
      mShaders["standardVS"]->GetBufferSize()};
  opaquePsoDesc.PS = {
      reinterpret_cast<BYTE*>(mShaders["opaquePS"]->GetBufferPointer()),
      mShaders["opaquePS"]->GetBufferSize()};
  opaquePsoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
  opaquePsoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
  opaquePsoDesc.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
  opaquePsoDesc.SampleMask = UINT_MAX;
  opaquePsoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
  opaquePsoDesc.NumRenderTargets = 1;
  opaquePsoDesc.RTVFormats[0] = mBackBufferFormat;
  opaquePsoDesc.SampleDesc.Count = m4xMsaaState ? 4 : 1;
  opaquePsoDesc.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
  opaquePsoDesc.DSVFormat = mDepthStencilFormat;
  ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(
      &opaquePsoDesc, IID_PPV_ARGS(&mPSOs["opaque"])));

  //
  // PSO for shadow map pass.
  //
  D3D12_GRAPHICS_PIPELINE_STATE_DESC smapPsoDesc = opaquePsoDesc;
  smapPsoDesc.RasterizerState.DepthBias = 100000;
  smapPsoDesc.RasterizerState.DepthBiasClamp = 0.0f;
  smapPsoDesc.RasterizerState.SlopeScaledDepthBias = 1.0f;
  smapPsoDesc.pRootSignature = mRootSignature.Get();
  smapPsoDesc.VS = {
      reinterpret_cast<BYTE*>(mShaders["shadowVS"]->GetBufferPointer()),
      mShaders["shadowVS"]->GetBufferSize()};
  smapPsoDesc.PS = {
      reinterpret_cast<BYTE*>(mShaders["shadowOpaquePS"]->GetBufferPointer()),
      mShaders["shadowOpaquePS"]->GetBufferSize()};

  // Shadow map pass does not have a render target.
  smapPsoDesc.RTVFormats[0] = DXGI_FORMAT_UNKNOWN;
  smapPsoDesc.NumRenderTargets = 0;
  ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(
      &smapPsoDesc, IID_PPV_ARGS(&mPSOs["shadow_opaque"])));

  //
  // PSO for debug layer.
  //
  D3D12_GRAPHICS_PIPELINE_STATE_DESC debugPsoDesc = opaquePsoDesc;
  debugPsoDesc.pRootSignature = mRootSignature.Get();
  debugPsoDesc.VS = {
      reinterpret_cast<BYTE*>(mShaders["debugVS"]->GetBufferPointer()),
      mShaders["debugVS"]->GetBufferSize()};
  debugPsoDesc.PS = {
      reinterpret_cast<BYTE*>(mShaders["debugPS"]->GetBufferPointer()),
      mShaders["debugPS"]->GetBufferSize()};
  ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(
      &debugPsoDesc, IID_PPV_ARGS(&mPSOs["debug"])));

  //
  // PSO for sky.
  //
  D3D12_GRAPHICS_PIPELINE_STATE_DESC skyPsoDesc = opaquePsoDesc;

  // The camera is inside the sky sphere, so just turn off culling.
  skyPsoDesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;

  // Make sure the depth function is LESS_EQUAL and not just LESS.
  // Otherwise, the normalized depth values at z = 1 (NDC) will
  // fail the depth test if the depth buffer was cleared to 1.
  skyPsoDesc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL;
  skyPsoDesc.pRootSignature = mRootSignature.Get();
  skyPsoDesc.VS = {
      reinterpret_cast<BYTE*>(mShaders["skyVS"]->GetBufferPointer()),
      mShaders["skyVS"]->GetBufferSize()};
  skyPsoDesc.PS = {
      reinterpret_cast<BYTE*>(mShaders["skyPS"]->GetBufferPointer()),
      mShaders["skyPS"]->GetBufferSize()};
  ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(
      &skyPsoDesc, IID_PPV_ARGS(&mPSOs["sky"])));
}
void DxGraphics::Shadows_UpdateObjectCBs() { UpdateObjectCBs_V2(); }
void DxGraphics::Shadows_UpdateMaterialBuffer() {
  auto currMaterialBuffer = mCurrFrameResource->MaterialBuffer.get();
  for (auto& e : mMaterials) {
    // Only update the cbuffer data if the constants have changed.  If the
    // cbuffer data changes, it needs to be updated for each FrameResource.
    Material* mat = e.second.get();
    if (mat->NumFramesDirty > 0) {
      XMMATRIX matTransform = XMLoadFloat4x4(&mat->MatTransform);

      MaterialData matData;
      matData.DiffuseAlbedo = mat->DiffuseAlbedo;
      matData.FresnelR0 = mat->FresnelR0;
      matData.Roughness = mat->Roughness;
      XMStoreFloat4x4(&matData.MatTransform, XMMatrixTranspose(matTransform));
      matData.DiffuseMapIndex = mat->DiffuseSrvHeapIndex;
      matData.NormalMapIndex = mat->NormalSrvHeapIndex;

      currMaterialBuffer->CopyData(mat->MatCBIndex, matData);

      // Next FrameResource need to be updated too.
      mat->NumFramesDirty--;
    }
  }
}
void DxGraphics::Shadows_UpdateShadowTransform() {
  // Only the first "main" light casts a shadow.
  XMVECTOR lightDir = XMLoadFloat3(&mRotatedLightDirections[0]);
  XMVECTOR lightPos = -2.0f * mSceneBounds.Radius * lightDir;
  XMVECTOR targetPos = XMLoadFloat3(&mSceneBounds.Center);
  XMVECTOR lightUp = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
  XMMATRIX lightView = XMMatrixLookAtLH(lightPos, targetPos, lightUp);

  XMStoreFloat3(&mLightPosW, lightPos);

  // Transform bounding sphere to light space.
  XMFLOAT3 sphereCenterLS;
  XMStoreFloat3(&sphereCenterLS, XMVector3TransformCoord(targetPos, lightView));

  // Ortho frustum in light space encloses scene.
  float l = sphereCenterLS.x - mSceneBounds.Radius;
  float b = sphereCenterLS.y - mSceneBounds.Radius;
  float n = sphereCenterLS.z - mSceneBounds.Radius;
  float r = sphereCenterLS.x + mSceneBounds.Radius;
  float t = sphereCenterLS.y + mSceneBounds.Radius;
  float f = sphereCenterLS.z + mSceneBounds.Radius;

  mLightNearZ = n;
  mLightFarZ = f;
  XMMATRIX lightProj = XMMatrixOrthographicOffCenterLH(l, r, b, t, n, f);

  // Transform NDC space [-1,+1]^2 to texture space [0,1]^2
  XMMATRIX T(0.5f, 0.0f, 0.0f, 0.0f, 0.0f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
      0.0f, 0.5f, 0.5f, 0.0f, 1.0f);

  XMMATRIX S = lightView * lightProj * T;
  XMStoreFloat4x4(&mLightView, lightView);
  XMStoreFloat4x4(&mLightProj, lightProj);
  XMStoreFloat4x4(&mShadowTransform, S);
}
void DxGraphics::Shadows_UpdateMainPassCB() {
  XMMATRIX view = mCamera.GetView();
  XMMATRIX proj = mCamera.GetProj();

  XMMATRIX viewProj = XMMatrixMultiply(view, proj);
  auto det = XMMatrixDeterminant(view);
  XMMATRIX invView = XMMatrixInverse(&det, view);
  det = XMMatrixDeterminant(proj);
  XMMATRIX invProj = XMMatrixInverse(&det, proj);
  det = XMMatrixDeterminant(viewProj);
  XMMATRIX invViewProj = XMMatrixInverse(&det, viewProj);

  XMMATRIX shadowTransform = XMLoadFloat4x4(&mShadowTransform);

  XMStoreFloat4x4(&mMainPassCB.View, XMMatrixTranspose(view));
  XMStoreFloat4x4(&mMainPassCB.InvView, XMMatrixTranspose(invView));
  XMStoreFloat4x4(&mMainPassCB.Proj, XMMatrixTranspose(proj));
  XMStoreFloat4x4(&mMainPassCB.InvProj, XMMatrixTranspose(invProj));
  XMStoreFloat4x4(&mMainPassCB.ViewProj, XMMatrixTranspose(viewProj));
  XMStoreFloat4x4(&mMainPassCB.InvViewProj, XMMatrixTranspose(invViewProj));
  XMStoreFloat4x4(
      &mMainPassCB.ShadowTransform, XMMatrixTranspose(shadowTransform));
  mMainPassCB.EyePosW = mCamera.GetPosition3f();
  mMainPassCB.RenderTargetSize =
      XMFLOAT2((float)mClientWidth, (float)mClientHeight);
  mMainPassCB.InvRenderTargetSize =
      XMFLOAT2(1.0f / mClientWidth, 1.0f / mClientHeight);
  mMainPassCB.NearZ = 1.0f;
  mMainPassCB.FarZ = 1000.0f;
  mMainPassCB.TotalTime = g_timer.TotalTime();
  mMainPassCB.DeltaTime = g_timer.DeltaTime();
  mMainPassCB.AmbientLight = {0.25f, 0.25f, 0.35f, 1.0f};
  mMainPassCB.Lights[0].Direction = mRotatedLightDirections[0];
  mMainPassCB.Lights[0].Strength = {0.9f, 0.8f, 0.7f};
  mMainPassCB.Lights[1].Direction = mRotatedLightDirections[1];
  mMainPassCB.Lights[1].Strength = {0.4f, 0.4f, 0.4f};
  mMainPassCB.Lights[2].Direction = mRotatedLightDirections[2];
  mMainPassCB.Lights[2].Strength = {0.2f, 0.2f, 0.2f};

  auto currPassCB = mCurrFrameResource->PassCB.get();
  currPassCB->CopyData(0, mMainPassCB);
}
void DxGraphics::Shadows_UpdateShadowPassCB() {
  XMMATRIX view = XMLoadFloat4x4(&mLightView);
  XMMATRIX proj = XMLoadFloat4x4(&mLightProj);

  XMMATRIX viewProj = XMMatrixMultiply(view, proj);
  auto det = XMMatrixDeterminant(view);
  XMMATRIX invView = XMMatrixInverse(&det, view);
  det = XMMatrixDeterminant(proj);
  XMMATRIX invProj = XMMatrixInverse(&det, proj);
  det = XMMatrixDeterminant(viewProj);
  XMMATRIX invViewProj = XMMatrixInverse(&det, viewProj);

  UINT w = mShadowMap->Width();
  UINT h = mShadowMap->Height();

  XMStoreFloat4x4(&mShadowPassCB.View, XMMatrixTranspose(view));
  XMStoreFloat4x4(&mShadowPassCB.InvView, XMMatrixTranspose(invView));
  XMStoreFloat4x4(&mShadowPassCB.Proj, XMMatrixTranspose(proj));
  XMStoreFloat4x4(&mShadowPassCB.InvProj, XMMatrixTranspose(invProj));
  XMStoreFloat4x4(&mShadowPassCB.ViewProj, XMMatrixTranspose(viewProj));
  XMStoreFloat4x4(&mShadowPassCB.InvViewProj, XMMatrixTranspose(invViewProj));
  mShadowPassCB.EyePosW = mLightPosW;
  mShadowPassCB.RenderTargetSize = XMFLOAT2((float)w, (float)h);
  mShadowPassCB.InvRenderTargetSize = XMFLOAT2(1.0f / w, 1.0f / h);
  mShadowPassCB.NearZ = mLightNearZ;
  mShadowPassCB.FarZ = mLightFarZ;

  auto currPassCB = mCurrFrameResource->PassCB.get();
  currPassCB->CopyData(1, mShadowPassCB);
}

std::array<const CD3DX12_STATIC_SAMPLER_DESC, 7>
DxGraphics::Shadows_GetStaticSamplers() {
  // Applications usually only need a handful of samplers.  So just define them
  // all up front and keep them available as part of the root signature.

  const CD3DX12_STATIC_SAMPLER_DESC pointWrap(0, // shaderRegister
      D3D12_FILTER_MIN_MAG_MIP_POINT,            // filter
      D3D12_TEXTURE_ADDRESS_MODE_WRAP,           // addressU
      D3D12_TEXTURE_ADDRESS_MODE_WRAP,           // addressV
      D3D12_TEXTURE_ADDRESS_MODE_WRAP);          // addressW

  const CD3DX12_STATIC_SAMPLER_DESC pointClamp(1, // shaderRegister
      D3D12_FILTER_MIN_MAG_MIP_POINT,             // filter
      D3D12_TEXTURE_ADDRESS_MODE_CLAMP,           // addressU
      D3D12_TEXTURE_ADDRESS_MODE_CLAMP,           // addressV
      D3D12_TEXTURE_ADDRESS_MODE_CLAMP);          // addressW

  const CD3DX12_STATIC_SAMPLER_DESC linearWrap(2, // shaderRegister
      D3D12_FILTER_MIN_MAG_MIP_LINEAR,            // filter
      D3D12_TEXTURE_ADDRESS_MODE_WRAP,            // addressU
      D3D12_TEXTURE_ADDRESS_MODE_WRAP,            // addressV
      D3D12_TEXTURE_ADDRESS_MODE_WRAP);           // addressW

  const CD3DX12_STATIC_SAMPLER_DESC linearClamp(3, // shaderRegister
      D3D12_FILTER_MIN_MAG_MIP_LINEAR,             // filter
      D3D12_TEXTURE_ADDRESS_MODE_CLAMP,            // addressU
      D3D12_TEXTURE_ADDRESS_MODE_CLAMP,            // addressV
      D3D12_TEXTURE_ADDRESS_MODE_CLAMP);           // addressW

  const CD3DX12_STATIC_SAMPLER_DESC anisotropicWrap(4, // shaderRegister
      D3D12_FILTER_ANISOTROPIC,                        // filter
      D3D12_TEXTURE_ADDRESS_MODE_WRAP,                 // addressU
      D3D12_TEXTURE_ADDRESS_MODE_WRAP,                 // addressV
      D3D12_TEXTURE_ADDRESS_MODE_WRAP,                 // addressW
      0.0f,                                            // mipLODBias
      8);                                              // maxAnisotropy

  const CD3DX12_STATIC_SAMPLER_DESC anisotropicClamp(5, // shaderRegister
      D3D12_FILTER_ANISOTROPIC,                         // filter
      D3D12_TEXTURE_ADDRESS_MODE_CLAMP,                 // addressU
      D3D12_TEXTURE_ADDRESS_MODE_CLAMP,                 // addressV
      D3D12_TEXTURE_ADDRESS_MODE_CLAMP,                 // addressW
      0.0f,                                             // mipLODBias
      8);                                               // maxAnisotropy

  const CD3DX12_STATIC_SAMPLER_DESC shadow(6,           // shaderRegister
      D3D12_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT, // filter
      D3D12_TEXTURE_ADDRESS_MODE_BORDER,                // addressU
      D3D12_TEXTURE_ADDRESS_MODE_BORDER,                // addressV
      D3D12_TEXTURE_ADDRESS_MODE_BORDER,                // addressW
      0.0f,                                             // mipLODBias
      16,                                               // maxAnisotropy
      D3D12_COMPARISON_FUNC_LESS_EQUAL, D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK);

  return {pointWrap, pointClamp, linearWrap, linearClamp, anisotropicWrap,
      anisotropicClamp, shadow};
}

CD3DX12_CPU_DESCRIPTOR_HANDLE DxGraphics::GetCpuSrv(int index) const {
  auto srv = CD3DX12_CPU_DESCRIPTOR_HANDLE(
      mSrvDescriptorHeap->GetCPUDescriptorHandleForHeapStart());
  srv.Offset(index, mCbvSrvUavDescriptorSize);
  return srv;
}

CD3DX12_GPU_DESCRIPTOR_HANDLE DxGraphics::GetGpuSrv(int index) const {
  auto srv = CD3DX12_GPU_DESCRIPTOR_HANDLE(
      mSrvDescriptorHeap->GetGPUDescriptorHandleForHeapStart());
  srv.Offset(index, mCbvSrvUavDescriptorSize);
  return srv;
}

CD3DX12_CPU_DESCRIPTOR_HANDLE DxGraphics::GetDsv(int index) const {
  auto dsv = CD3DX12_CPU_DESCRIPTOR_HANDLE(
      mDsvHeap->GetCPUDescriptorHandleForHeapStart());
  dsv.Offset(index, mDsvDescriptorSize);
  return dsv;
}

CD3DX12_CPU_DESCRIPTOR_HANDLE DxGraphics::GetRtv(int index) const {
  auto rtv = CD3DX12_CPU_DESCRIPTOR_HANDLE(
      mRtvHeap->GetCPUDescriptorHandleForHeapStart());
  rtv.Offset(index, mRtvDescriptorSize);
  return rtv;
}


} // namespace ifire